現在大部分的功能都已經實作好了,剩下將資料儲存到手機,讓使用者下次開啟App時可以看到過去儲存的資料,有許多方法可以使用,由於我們的資料簡單,所以我打算使用UserDefault
就好,由於我們的資料是結構體(struct),要儲存的話需要進行編碼及解碼,所以先讓ListModel遵從Codable協定,這部分在Day2建立資料模型的時候就已經實作過了:
struct ListModel:Codable {
var title = ""
var subtitle = ""
}
由於儲存資料模型的地方是在ListViewController
內,所以我將儲存的方法也寫在這裏:
func saveData()
{
let encoder = JSONEncoder()
if let encodeListData = try? encoder.encode(listDatas)
{
UserDefaults.standard.set(encodeListData, forKey: "listData")
}
}
上述這段程式碼的意思是,先初始化一個JSON編碼器,並且將我的listDatas
編碼後,儲存進UserDefaults
,儲存的key是listData
,那我下次讀取時也要使用相同的key才能夠讀取到這筆資料。
儲存的方法寫完了,那要在哪裡呼叫呢??我的答案是,只要有動到資料的地方,就要儲存資料,例如新增、插入、刪除、修改。
所以我在各個動作內都呼叫這個方法,首先是新增跟插入,都是點選確認按鈕才算資料進行更動,所以我在確認按鈕內呼叫saveData(),所以你的UIAlertController內的okAction應該像這樣子:
let okAction = UIAlertAction(title: actionTitle, style: .destructive){ (action) in
if let text0 = alert.textFields?[0].text, let text1 = alert.textFields?[1].text
{
let totalText = ListModel(title: text0, subtitle: text1)
if alertTitle == "新增"
{
self.listDatas.insert(totalText, at: 0)
self.toDoListTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
}
if let indexPath = indexPath{
if alertTitle == "插入"
{
self.listDatas.insert(totalText, at: indexPath.row)
self.toDoListTableView.insertRows(at: [indexPath], with: .automatic)
}
}
}
self.saveData()
}
另外在往左滑帶出選項時的刪除按鍵跟修改按鍵,我也是在這兩個按鍵內都呼叫saveData(),所以現在你的刪除按鍵跟修改按鍵的程式碼應該長這樣子:
let deleteAction = UIContextualAction(style: .destructive, title: "刪除") { (action, sourceView, complete) in
self.listDatas.remove(at: indexPath.row)
self.toDoListTableView.deleteRows(at: [indexPath], with: .top)
self.saveData()
complete(true)
}
deleteAction.image = UIImage(systemName: "trash")
let modifyAction = UIContextualAction(style: .normal, title: "修改") { (action, sourceView, complete) in
let oldValue = self.listDatas[indexPath.row]
self.indexPath = indexPath
let destinationVC = self.storyboard?.instantiateViewController(identifier: "ModifyViewController") as! ModifyViewController
destinationVC.listData = oldValue
destinationVC.passingToATVCClosure = {(listData) in
self.listDatas[indexPath.row] = listData
self.saveData()
}
self.show(destinationVC, sender: self)
complete(true)
}
現在你的資料已經可以儲存到手機本地了,但是目前還缺乏讀取的實作,否則即使儲存了,下次使用者打開App,如果沒有讀取功能則依然看不到資料。
所以我們一樣在ListViewController內實作一個讀取的方法,我命名為loadData():
func loadData()
{
if let loadData = UserDefaults.standard.object(forKey: "listData") as? Data
{
let decoder = JSONDecoder()
if let listData = try! decoder.decode([ListModel]?.self, from: loadData)
{
self.listDatas = listData
toDoListTableView.reloadData()
}
}
}
上面這段程式碼是使用"listData"
這個key,去讀取資料並轉型為Data,並且當我讀取到資料後,使用JSONDecoder
解碼器,解碼loadData
這個資料,並且解碼為[ListModel]
格式,再把解完的資料,指定給我自己的listDatas
,然後記得要重整表格頁面,否則表格顯示的資料不會改變。
最後我將這個loadData()方法,放在生命週期viewDidLoad()內被呼叫,所以只要每次程式一載入進來,就會重新整理資料,現在你的ListViewController內的viewDidLoad()應該長這樣子:
override func viewDidLoad() {
super.viewDidLoad()
loadData()
toDoListTableView.delegate = self
toDoListTableView.dataSource = self
}
現在已經完成正常功能的所有實作了,接下來屬於比較偏動畫類的...我還記得這些動畫類的實作做完後,工作室的學長們跟我說:「Jimmy,你的App怎麼這麼"虛華"」,不管!我就是爽啦!